package client

import (
	"base"
	"gate_server/chanrpc"
	"gate_server/logic/center/api"
	"gate_server/logic/client/api"
	"gate_server/logic/game/api"
	. "gate_server/logic/internal"
	"gate_server/logic/player/event"
	"gate_server/model"
	"leaf/gate"
	"proto/base"
	"proto/player"
	"proto/server/center"
	"proto/server/gate"
)

type clientSystem struct {
	curId   base.ClientId
	clients map[base.ClientId]*model.Client
	agents  map[gate.Agent]*model.Client
}

var (
	system = new(clientSystem)
)

func init() {
	system.clients = make(map[base.ClientId]*model.Client)
	system.agents = make(map[gate.Agent]*model.Client)
}

func init() {
	client_api.Get = system.Get
}

func init() {
	Event.Listen(player_event.OnLogin, system.onLogin)
	Event.Listen(player_event.OnLogout, system.onLogout)
}

func init() {
	Skeleton.AfterInit(func() {
		Skeleton.RegisterChanRPC(chanrpc.NewClientAgent, system.newAgent)
		Skeleton.RegisterChanRPC(chanrpc.CloseClientAgent, system.closeAgent)
	})
}

func (*clientSystem) Get(clientId base.ClientId) (result *model.Client, exists bool) {
	result, exists = system.clients[clientId]
	return
}

func (*clientSystem) logout(client *model.Client) {
	if player := client.Player(); player != nil {
		msg := new(proto_server_center.Logout)
		msg.ClientId = client.Id()
		center_api.WriteMsg(msg)

		game_api.Logout(player)
	}
}

func (*clientSystem) onLogin(msg *proto_server_gate.OnLogin, player *model.Player) {
	clientId := base.ClientId(msg.ClientId)
	client, exists := system.Get(clientId)
	if !exists {
		return
	}

	if msg.Code == proto_base.ErrorCode_Success {
		player.SetClient(client)
		client.SetPlayer(player)
	}

	response := new(proto_player.OnLogin)
	response.Code = msg.Code
	response.PlayerId = msg.PlayerId
	client.WriteMsg(response)
}

func (*clientSystem) onLogout(player *model.Player, reason proto_base.LogoutReason) {
	if client := player.Client(); client != nil {
		client.SetPlayer(nil)

		response := new(proto_player.OnLogout)
		response.Reason = reason
		client.WriteMsg(response)
	}
}

func (*clientSystem) newAgent(args []interface{}) {
	agent := args[0].(gate.Agent)
	base.Logger.Debug("new client agent, remote address: %s, local address: %s", agent.RemoteAddr(), agent.LocalAddr())

	clientId := system.generateId()
	client := model.NewClient(agent, clientId)
	system.clients[clientId] = client
	system.agents[agent] = client

	agent.SetUserData(client)
}

func (*clientSystem) closeAgent(args []interface{}) {
	agent := args[0].(gate.Agent)
	base.Logger.Debug("close client agent, remote address: %s, local address: %s", agent.RemoteAddr(), agent.LocalAddr())

	client := agent.UserData().(*model.Client)
	delete(system.agents, agent)
	delete(system.clients, client.Id())

	agent.SetUserData(nil)
	system.logout(client)
}

func (*clientSystem) generateId() (result base.ClientId) {
	result = system.curId
	for {
		if _, exists := system.Get(result); exists {
			system.curId++
		} else {
			system.curId = result + 1
			return
		}
	}
}
